package com.jboltai.capability.vdb.types.tencent;


import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.jboltai.capability.embedding.JBoltEmbedding;
import com.jboltai.capability.vdb.JBoltVDB;
import com.jboltai.capability.vdb.VDBOptResult;
import com.jboltai.capability.vdb.collection.VDBCollection;
import com.jboltai.capability.vdb.collection.field.VDBField;
import com.jboltai.capability.vdb.collection.field.VDBFieldType;
import com.jboltai.capability.vdb.doc.VDBDoc;
import com.jboltai.capability.vdb.search.ConsistencyLevel;
import com.jboltai.capability.vdb.search.VDBConditions;
import com.jboltai.capability.vdb.search.VDBFilter;
import com.jboltai.config.JBoltAIConfig;
import com.jboltai.event.EventError;
import com.jboltai.event.embedding.EmbeddingEvent;
import com.jboltai.resource.embedding.EmbeddingModel;
import com.jboltai.resource.vdb.VDBResource;
import com.tencent.tcvectordb.client.VectorDBClient;
import com.tencent.tcvectordb.exception.VectorDBException;
import com.tencent.tcvectordb.model.Collection;
import com.tencent.tcvectordb.model.DocField;
import com.tencent.tcvectordb.model.Document;
import com.tencent.tcvectordb.model.param.collection.*;
import com.tencent.tcvectordb.model.param.dml.*;
import com.tencent.tcvectordb.model.param.entity.AffectRes;
import com.tencent.tcvectordb.model.param.entity.BaseRes;
import com.tencent.tcvectordb.model.param.enums.EmbeddingModelEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * jbolt 腾讯向量数据库工具类
 */
public class JBoltTencentVDB extends JBoltVDB {

    private static final Logger LOGGER = LoggerFactory.getLogger(JBoltAIConfig.LOGGER_NAME);

    /**
     * 集合中向量字段的维度值
     */
    private ConcurrentHashMap<String, Collection> COLLECTION_CACHE = new ConcurrentHashMap<>();

    /**
     * 数据库
     */
    private String database;


    private VectorDBClient client;

    /**
     * 是否使用腾讯内置的embedding功能
     */
    private boolean useInnerEmbedding = false;


    public JBoltTencentVDB(VDBResource resource) {
        super(resource);
        this.client = ((TencentVDBResource) resource).getClient();
        if (StrUtil.isBlank(resource.getDatabase())) {
            throw new RuntimeException("向量数据库资源[" + resource.getId() + "]未设置database");
        }
        this.database = resource.getDatabase();
        this.useInnerEmbedding = EmbeddingModel.isTencentInnerModel(resource.getEmbeddingModel());

    }


    @Override
    public VDBOptResult<Boolean> createCollection(VDBCollection collection) {
        if (resource.getEmbeddingModel() == null) {
            throw new RuntimeException("向量数据库[" + resource.getId() + "]未指定Embedding模型");
        }

        try {
            Integer shardNum = collection.getShardNum();
            if (shardNum == null) {
                shardNum = this.resource.getDefaultShardNum();
            }
            Integer replicaNum = collection.getReplicaNum();
            if (replicaNum == null) {
                replicaNum = this.resource.getDefaultReplicaNum();
            }
            CreateCollectionParam.Builder builder = CreateCollectionParam.newBuilder()
                    .withName(collection.getName())
                    .withShardNum(shardNum)
                    .withReplicaNum(replicaNum);
            if (collection.getDescription() != null) {
                builder.withDescription(collection.getDescription());
            }
            for (VDBField field : collection.getFields()) {
                FieldType type;
                switch (field.getVdbFieldType()) {
                    case STRING:
                        type = FieldType.String;
                        break;
                    case NUMBER32:
                    case NUMBER64:
                        type = FieldType.Uint64;
                        break;
                    case VECTOR:
                        type = FieldType.Vector;
                        break;
                    default:
                        throw new RuntimeException("不支持的字段类型:" + field.getVdbFieldType());
                }
                if (field.isPrimaryKey()) {
                    //主键字段
                    builder.addField(new FilterIndex(field.getName(), type, IndexType.PRIMARY_KEY));
                } else if (field.getVdbFieldType() == VDBFieldType.VECTOR) {
                    if (useInnerEmbedding) {
                        processInnerVectorField(builder, field);
                    }
                    processVectorField(builder, field);
                } else {
                    if (field.getName().equals(VDBField.DOC_FIELD_NAME) && useInnerEmbedding) {
                        //如果用的内置embedding，doc这个字段就不用单独添加了，embedding已经处理了
                    } else {
                        //普通字段
                        builder.addField(new FilterIndex(field.getName(), type, IndexType.FILTER));
                    }
                }
            }
            client.database(database).createCollection(builder.build());

            return new VDBOptResult<>(true, true);
        } catch (Exception e) {
            return new VDBOptResult<>(e);
        }

    }

    /**
     * 处理向量字段
     *
     * @param builder
     * @param field
     */
    private void processVectorField(CreateCollectionParam.Builder builder, VDBField field) {
        //向量字段
        MetricType metricType;
        VectorIndex vectorIndex;
        switch (field.getVdbMetricType()) {
            case DEFAULT:
                metricType = MetricType.COSINE;
                break;
            case L2:
                metricType = MetricType.L2;
                break;
            case IP:
                metricType = MetricType.IP;
                break;
            default:
                throw new RuntimeException("不支持的向量索引类型:" + field.getVectorIndexType());
        }
        JSONObject params = field.getVectorIndexParams();

        IndexType indexType;
        switch (field.getVectorIndexType()) {
            case FLAT:
                indexType = IndexType.FLAT;
                vectorIndex = new VectorIndex(field.getName(), resource.getEmbeddingModel().getDimension(), indexType,
                        metricType, null);
                break;
            case HNSW: {
                indexType = IndexType.HNSW;
                Integer m = params == null ? null : params.getInteger("M");
                Integer efConstruction = params == null ? null : params.getInteger("efConstruction");
                vectorIndex = new VectorIndex(field.getName(), resource.getEmbeddingModel().getDimension(), indexType,
                        metricType, new HNSWParams(m == null ? 16 : m, efConstruction == null ? 200 : efConstruction));
                break;
            }


            case IVF_FLAT: {
                if (params == null || params.containsKey("nlist") == false) {
                    throw new RuntimeException("IVF_FLAT类型的向量字段必须指定nlist参数");
                }
                int nlist = params.getIntValue("nlist");
                indexType = IndexType.IVF_FLAT;
                vectorIndex = new VectorIndex(field.getName(), resource.getEmbeddingModel().getDimension(), indexType,
                        metricType, new IVFFLATParams(nlist));
                break;
            }

            case IVF_PQ: {
                if (params == null) {
                    throw new RuntimeException("IVF_PQ类型的向量字段必须指定参数");
                }
                Integer nlist = params.getInteger("nlist");
                if (nlist == null) {
                    throw new RuntimeException("IVF_PQ类型的向量字段必须指定nlist参数");
                }
                Integer m = params.getInteger("M");
                if (m == null) {
                    throw new RuntimeException("IVF_PQ类型的向量字段必须指定M参数");
                }
                indexType = IndexType.IVF_PQ;
                vectorIndex = new VectorIndex(field.getName(), resource.getEmbeddingModel().getDimension(), indexType,
                        metricType, new IVFPQParams(nlist, m));
                break;
            }
            case IVF_SQ8: {
                if (params == null || params.containsKey("nlist") == false) {
                    throw new RuntimeException("IVF_SQ8类型的向量字段必须指定nlist参数");
                }
                int nlist = params.getIntValue("nlist");
                indexType = IndexType.IVF_SQ8;
                vectorIndex = new VectorIndex(field.getName(), resource.getEmbeddingModel().getDimension(), indexType,
                        metricType, new IVFSQ8Params(nlist));
                break;
            }
            default:
                throw new RuntimeException("不支持的向量索引类型:" + field.getVectorIndexType());
        }

//                new VectorIndex("vector", field.getDimension(), indexType,
//                        metricType, new HNSWParams(16, 200));
        builder.addField(vectorIndex);
    }

    /**
     * 腾讯内部embedding
     * @param builder
     * @param field
     */
    private void processInnerVectorField(CreateCollectionParam.Builder builder, VDBField field) {
        Embedding.Builder embeding = Embedding.newBuilder();
        if (resource.getEmbeddingModel() == EmbeddingModel.TENCENT_BGE_BASE_ZH) {
            embeding.withModel(EmbeddingModelEnum.BGE_BASE_ZH);
        } else if (resource.getEmbeddingModel() == EmbeddingModel.TENCENT_M3E_BASE) {
            embeding.withModel(EmbeddingModelEnum.M3E_BASE);
        } else if (resource.getEmbeddingModel() == EmbeddingModel.TENCENT_E5_LARGE_V2) {
            embeding.withModel(EmbeddingModelEnum.E5_LARGE_V2);
        } else if (resource.getEmbeddingModel() == EmbeddingModel.TENCENT_TEXT2VEC_LARGE_CHINESE) {
            embeding.withModel(EmbeddingModelEnum.TEXT2VEC_LARGE_CHINESE);
        } else if (resource.getEmbeddingModel() == EmbeddingModel.TENCENT_MULTILINGUAL_E5_BASE) {
            embeding.withModel(EmbeddingModelEnum.MULTILINGUAL_E5_BASE);
        } else {
            throw new RuntimeException("不支持的腾讯内部模型:" + resource.getEmbeddingModel());
        }
        Embedding embedding = embeding.withField(VDBField.DOC_FIELD_NAME).withVectorField(VDBField.VECTOR_FIELD_NAME).build();
        builder.withEmbedding(embedding);
    }


    @Override
    public VDBOptResult<Boolean> dropCollection(String collectionName) {
        try {

            client.database(database).dropCollection(collectionName);
            COLLECTION_CACHE.remove(collectionName);
            return new VDBOptResult<>(true, true);
        } catch (Exception e) {
            return new VDBOptResult<>(e);
        }
    }


    private Collection getCollection(String collectionName) {
        Collection collection = COLLECTION_CACHE.get(collectionName);
        if (collection == null) {
            collection = client.database(database).describeCollection(collectionName);
            COLLECTION_CACHE.put(collectionName, collection);
        }
        return collection;
    }

    @Override
    public VDBOptResult<Boolean> checkDatabaseExist(String databaseName) {
        try {
            List<String> databases = client.listDatabase();
            return new VDBOptResult<>(true, databases != null && !databases.isEmpty() && databases.contains(databaseName));
        } catch (Exception e) {
            return new VDBOptResult<>(e);
        }
    }

    @Override
    public VDBOptResult<List<VDBCollection>> listCollections() {
        try {
            List<Collection> collections = client.database(database).listCollections();
            if (collections == null || collections.isEmpty()) {
                return new VDBOptResult<>(true, Collections.emptyList());
            }

            return new VDBOptResult<>(true, collections.stream().map(item -> {
                VDBCollection collection = new VDBCollection(this);
                collection.setName(item.getCollection());
                collection.setDescription(item.getDescription());
                collection.setShardNum(item.getShardNum());
                collection.setReplicaNum(item.getReplicaNum());
                return collection;
            }).collect(Collectors.toList()));
        } catch (Exception e) {
            return new VDBOptResult<>(e);
        }

    }

    @Override
    public VDBOptResult<Boolean> checkCollectionExist(String collectionName) {
        try {

            VDBOptResult<List<VDBCollection>> result = listCollections();
            if (!result.isSuccess()) {
                return new VDBOptResult<>(result.getMsg(), result.getException());
            }
            List<VDBCollection> collections = result.getResult();
            if (collections == null || collections.isEmpty()) {
                return new VDBOptResult<>(true, false);
            }
            return new VDBOptResult<>(true,
                    collections.stream().anyMatch(c -> c.getName().equals(collectionName)));
        } catch (Exception e) {
            return new VDBOptResult<>(e);
        }
    }

    @Override
    public VDBOptResult<Boolean> truncateCollection(String collectionName) {
        try {

            AffectRes res = client.database(database).truncateCollections(collectionName);
            if (res.getCode() != 0) {
                return new VDBOptResult<>(res.getMsg());
            }
            return new VDBOptResult<>(true, true);
        } catch (Exception e) {
            return new VDBOptResult<>(e);
        }
    }


    @Override
    public VDBOptResult<Integer> insert(String collectionName, VDBDoc... docs) {
        if (docs == null || docs.length == 0) {
            throw new IllegalArgumentException("docs不能为空");
        }
        AffectRes result = null;
        InsertParam insertParam = null;
        InsertParam.Builder paramBuilder = null;
        try {
            VDBOptResult<Document> generateResult = null;
            //用的是否是腾讯内部embedding
            paramBuilder = InsertParam.newBuilder();
            for (VDBDoc doc : docs) {

                generateResult = generateDocument(doc);
                if (!generateResult.isSuccess()) {
                    return new VDBOptResult<>(generateResult.getMsg(), generateResult.getException());
                }
                Document item = generateResult.getResult();
                paramBuilder.addDocument(item);
            }

            insertParam = paramBuilder
                    .withBuildIndex(true)
                    .build();
            result = client.database(database).collection(collectionName).upsert(insertParam);
            if (result.getCode() != 0) {
                return new VDBOptResult<>(result.getMsg());
            } else {
                return new VDBOptResult<>(true, (int)result.getAffectedCount());
            }
        } catch (VectorDBException e) {
            if (e.getMessage().contains("\"code\":13100")) {
                // TODO 试验后发现腾讯ivf索引根本没法用
                // 如果创建 Collection 选择的索引类型为 IVF 系列：当第一次写入时，当前集合还没有向量索引，此时 BuildIndex 必须为 false。
                // 插入原始数据之后，需通过 rebuildIndex()训练数据并重建索引。
                try {
                    //第一次写入
                    insertParam = paramBuilder.withBuildIndex(false).build();
                    result = client.database(database).collection(collectionName).upsert(insertParam);
                    if (result.getCode() != 0) {
                        return new VDBOptResult<>(result.getMsg());
                    }
                    // 重建索引
                    RebuildIndexParam rebuildIndexParam = RebuildIndexParam.newBuilder()
                            .build();
                    BaseRes buildRes = client.database(database).collection(collectionName).rebuildIndex(rebuildIndexParam);
                    if (buildRes.getCode() != 0) {
                        return new VDBOptResult<>(result.getMsg());
                    } else {
                        return new VDBOptResult<>(true, (int)result.getAffectedCount());
                    }
                } catch (Exception e2) {
                    return new VDBOptResult<>(e2);
                }

            } else {
                return new VDBOptResult<>(e);
            }
        } catch (Exception e) {
            return new VDBOptResult<>(e);
        }


    }

    private VDBOptResult<Document> generateDocument(VDBDoc doc) {

        VDBOptResult<Document> result = new VDBOptResult<>();
        Document.Builder builder = Document.newBuilder();
        if (doc.getId() != null) {
            builder.withId(doc.getId());
        }
        if (doc.getFieldValues() != null) {
            Iterator<String> iterator = doc.getFieldValues().keySet().iterator();
            while (iterator.hasNext()) {
                String key = iterator.next();
                builder.addDocField(new DocField(key, doc.getFieldValues().get(key)));
            }
        }
        if (doc.getDoc() != null) {
            builder.addDocField(new DocField(VDBField.DOC_FIELD_NAME, doc.getDoc()));

            if (useInnerEmbedding == false) {

                EmbeddingEvent embedding = JBoltEmbedding.embedding(resource.getEmbeddingModel(), doc.getDoc());
                embedding.setAttrs("result", result);
                embedding.setAttrs("builder", builder);
                embedding.onSuccess((event, res) -> {
                    Document.Builder b = event.getAttr("builder");
                    VDBOptResult<Document> r = event.getAttr("result");
                    b.withVector(res.get(0));
                    r.setSuccess(true);
                }).onFail((event, err) -> {
                    VDBOptResult<Document> r = event.getAttr("result");
                    r.setSuccess(false);
                    result.setMsg("Embedding发生异常：" + err.getMsg());
                    result.setException(err.getException());
                }).publish().await();

                if (result.isSuccess() == false) {
                    return result;
                }

            }
        }


        result.setSuccess(true);
        result.setResult(builder.build());
        return result;
    }





    @Override
    public VDBOptResult<List<VDBDoc>> search(String collectionName, VDBConditions conditions) {
        if (conditions == null) {
            conditions = new VDBConditions();
        }
        if (collectionName == null) {
            throw new RuntimeException("未指定collectionName");
        }

        try {
            OperationBuilder builder = null;
            if (conditions.getMatchText() == null || conditions.getMatchText().isEmpty()) {
                //精确查找
                builder = new OperationBuilder(QueryParam.newBuilder());
            } else {
                if (useInnerEmbedding) {
                    builder = new OperationBuilder(SearchByEmbeddingItemsParam.newBuilder());
                } else {
                    //基于向量的相似性查找
                    builder = new OperationBuilder(SearchByVectorParam.newBuilder());
                }
            }
            VDBOptResult<Boolean> buildResult = processBuilder(collectionName, conditions, builder);
            if (buildResult.isSuccess() ==false) {
                return new VDBOptResult<>(buildResult.getMsg(), buildResult.getException());
            }
            List<Document> docs = builder.search(client.database(database).collection(collectionName));
            if (docs == null || docs.isEmpty()) {
                return new VDBOptResult<>(true, Collections.EMPTY_LIST);
            }
            List<VDBDoc> list = new ArrayList<>(docs.size());
            for (Document doc : docs) {
                VDBDoc item = new VDBDoc(doc.getId(), doc.getDoc());
                item.setScore(doc.getScore());
                try {
                    item.setVector(doc.getVector());
                } catch (NullPointerException e) {
                    //调getVector都能报错，连空指针判断都不做，凸(艹皿艹 )
                }
                if (doc.getDocFields() != null) {
                    for (DocField field : doc.getDocFields()) {
                        item.setField(field.getName(), field.getValue());
                    }
                }
                list.add(item);
            }

            return new VDBOptResult<>(true, list);
        } catch (Exception e) {
            return new VDBOptResult<>(e);
        }

    }

    /**
     * 精准查询
     *
     * @param collectionName
     * @param conditions
     * @return
     */
    private VDBOptResult<Boolean> processBuilder(String collectionName, VDBConditions conditions, OperationBuilder builder) {
        if (conditions.getFilter() != null) {
            processFilter(builder, conditions.getFilter());
        }
        if (conditions.getConsistencyLevel() != null) {
            builder.withConsistencyLevel(conditions.getConsistencyLevel());
        }
        if (conditions.getSelect() != null) {
            //处理select字段
            List<String> selects = new ArrayList<>();
            for (String s : conditions.getSelect()) {
                String[] split = s.split(",");//用户可能传入["id,name"]
                for (String sub : split) {
                    sub = sub.trim();
                    if (StrUtil.isBlank(sub)) continue;

                    if (sub.equals(VDBField.VECTOR_FIELD_NAME)) {
                        //要输出 向量，就要设置retrieveVector为true
                        builder.withRetrieveVector(true);
                    } else {
                        selects.add(sub);
                    }
                }
            }
            builder.withOutputFields(selects);
        }
        if (conditions.getMatchText() != null && conditions.getMatchText().isEmpty() == false) {
            builder.withVectors(resource.getEmbeddingModel(), conditions.getMatchText());
            //向量化可能出错
            if (builder.eventError != null) {
                return new VDBOptResult<>("Embedding发生异常：" + builder.eventError.getMsg(), builder.eventError.getException());
            }
            builder.withParams(getCollection(collectionName), conditions);
        }
        if (conditions.getLimit() != null) {
            builder.withLimit(conditions.getLimit());
        }
        if (conditions.getOffset() != null) {
            builder.withOffset(conditions.getOffset());
        }
        return new VDBOptResult<>(true, true);

    }

    private void processFilter(OperationBuilder builder, List<VDBFilter> filter) {
        String link = "";
        StringBuilder cond = new StringBuilder();
        for (VDBFilter item : filter) {
            //id字段
            if (item.getName().equals(VDBField.ID_FIELD_NAME)) {
                switch (item.getOpt()) {
                    case EQ:
                        builder.withDocumentIds(Arrays.asList((String) item.getValue()));
                        continue;
                    case IN:
                        builder.withDocumentIds(Arrays.asList((String[]) item.getValue()));
                        continue;
                    default:
                        throw new RuntimeException("id不支持" + item.getOpt() + "操作");
                }
            }
            if (link != null) {
                cond.append(link);
            } else {
                cond.append(" and ");
            }
            switch (item.getOpt()) {
                case AND:
                    link = " and ";
                    continue;
                case OR:
                    link = " or ";
                    continue;
                case AND_NOT:
                    link = " and not ";
                    continue;
                case OR_NOT:
                    link = " or not ";
                    continue;
                case BRACKET_LEFT:
                    link = " (";
                    continue;
                case BRACKET_RIGHT:
                    link = " )";
                    continue;
                case EQ:
                    cond.append(item.getName()).append("=").append(parseValue(item));
                    break;
                case NOT_EQ:
                    cond.append(item.getName()).append("!=").append(parseValue(item));
                    break;
                case GT:
                    cond.append(item.getName()).append(">").append(parseValue(item));
                    break;
                case GE:
                    cond.append(item.getName()).append(">=").append(parseValue(item));
                    break;
                case LT:
                    cond.append(item.getName()).append("<").append(parseValue(item));
                    break;
                case LE:
                    cond.append(item.getName()).append("<=").append(parseValue(item));
                    break;
                case IN:
                    cond.append(item.getName()).append(" in (").append(parseValue(item)).append(")");
                    break;
                case NOT_IN:
                    cond.append(item.getName()).append(" not in (").append(parseValue(item)).append(")");
                    break;
                default:
                    throw new RuntimeException("不支持的过滤条件:" + item.getOpt());
            }

            link = null;
        }
        builder.withFilter(new Filter(cond.toString()));
    }

    private String parseValue(VDBFilter filter) {
        if (filter.getValue() == null) {
            throw new RuntimeException(filter.getName() + "的值不能为null");
        }
        Object value = filter.getValue();
        if (value instanceof String) {
            return "\"" + value + "\"";
        } else if (value instanceof String[]) {
            return Arrays.stream(((String[]) value)).map(x -> "\"" + x + "\"").collect(Collectors.joining(","));
        } else {
            return value.toString();
        }
    }


    @Override
    public VDBOptResult<Boolean> delete(String collectionName, VDBConditions conditions) {

        if (collectionName == null) {
            throw new RuntimeException("未指定collectionName");
        }
        if (conditions == null) {
            conditions = new VDBConditions();
        }
        try {

            OperationBuilder builder = new OperationBuilder(DeleteParam.newBuilder());
            processBuilder(collectionName, conditions, builder);

            int rows = builder.delete(client.database(database).collection(collectionName));
            return new VDBOptResult<Boolean>(true, true);
        } catch (Exception e) {
            return new VDBOptResult<Boolean>(e);
        }


    }

    @Override
    public VDBOptResult<Integer> update(String collectionName, VDBDoc doc, VDBConditions conditions) {
        if (conditions == null) {
            conditions = new VDBConditions();
        }
        if (doc.getId() != null) {
            conditions.eq(VDBField.ID_FIELD_NAME, doc.getId());
            //更新的时候，id字段不能跟着变，所以要把它清空
            doc.setId(null);
        }
        try {

            VDBOptResult<Document> generateResult = generateDocument(doc);
            if (generateResult.isSuccess() == false) {
                return new VDBOptResult<>(generateResult.getMsg(), generateResult.getException());
            }
            Document item = generateResult.getResult();
            OperationBuilder builder = new OperationBuilder(UpdateParam.newBuilder());
            VDBOptResult<Boolean> buildResult = processBuilder(collectionName, conditions, builder);
            if (buildResult.isSuccess() ==false) {
                return new VDBOptResult<>(buildResult.getMsg(), buildResult.getException());
            }
            int rows = builder.update(client.database(database).collection(collectionName), item);
            return new VDBOptResult<>(true, rows);
        } catch (Exception e) {
            return new VDBOptResult<>(e);
        }
    }


    @Override
    public VDBOptResult<Void> createDatabase(String database) {
        try {
            client.createDatabase(database);
            return new VDBOptResult<>(true, null);
        } catch (Exception e) {
            return new VDBOptResult<>(e);
        }

    }

    @Override
    public VDBOptResult<Boolean> dropDatabase(String database) {
        try {
            client.dropDatabase(database);
            return new VDBOptResult<>(true, true);
        } catch (Exception e) {
            return new VDBOptResult<>(e);
        }
    }

    private static class OperationBuilder {
        private QueryParam.Builder queryBuilder;

        private SearchByVectorParam.Builder searchBuilder;

        private DeleteParam.Builder deleteBuilder;

        private UpdateParam.Builder updateBuilder;

        private SearchByEmbeddingItemsParam.Builder innerEmbeddingBuilder;

        EventError eventError;

        public OperationBuilder(QueryParam.Builder queryBuilder) {
            this.queryBuilder = queryBuilder;
        }

        public OperationBuilder(SearchByVectorParam.Builder searchBuilder) {
            this.searchBuilder = searchBuilder;
        }

        public OperationBuilder(DeleteParam.Builder builder) {
            this.deleteBuilder = builder;
        }

        public OperationBuilder(UpdateParam.Builder builder) {
            this.updateBuilder = builder;
        }

        public OperationBuilder(SearchByEmbeddingItemsParam.Builder builder) {
            this.innerEmbeddingBuilder = builder;
        }

        public OperationBuilder withVectors(EmbeddingModel model, List<String> vectorText) {
            if (searchBuilder != null) {
                EmbeddingEvent event = JBoltEmbedding.embedding(model, vectorText);
                event.setAttrs("builder",this);
                event.onSuccess((e, res) -> {
                    searchBuilder.withVectors(res);
                }).onFail((e, error) -> {
                    OperationBuilder builder = event.getAttr("builder");
                    builder.eventError = error;
                }).publish().await();

            } else if (innerEmbeddingBuilder != null) {
                innerEmbeddingBuilder.withEmbeddingItems(vectorText);
            } else if (queryBuilder != null) {
                throw new RuntimeException("精确查询不支持向量匹配");
            } else if (deleteBuilder != null) {
                throw new RuntimeException("删除操作不支持向量匹配");
            } else if (updateBuilder != null) {
                throw new RuntimeException("更新操作不支持向量匹配");
            }
            return this;
        }

        public OperationBuilder withParams(Collection collection, VDBConditions  conditions) {
            IndexField index = null;
            for (IndexField collectionIndex : collection.getIndexes()) {
                if (collectionIndex.getFieldName().equals(VDBField.VECTOR_FIELD_NAME)) {
                    index = collectionIndex;
                    break;
                }
            }
            Params params = null;
            switch (index.getIndexType()) {
                case HNSW:
                    Integer ef = conditions.getVectorParam("ef", Integer.class);
                    if (ef != null) {
                        params = new HNSWSearchParams(ef);
                    }
                    break;
                case IVF_FLAT:
                case IVF_PQ:
                case IVF_SQ8:
                    Integer nprobe = conditions.getVectorParam("nprobe", Integer.class);
                    if (nprobe != null) {
                        params = GeneralParams.newBuilder().withNProbe(nprobe).build();
                    }
                    break;
            }
            if (params == null) {
                return this;
            }

            if (searchBuilder != null) {
                searchBuilder.withParams(params);
            } else if (innerEmbeddingBuilder != null) {
                innerEmbeddingBuilder.withParams(params);
            }
            return this;
        }

        public OperationBuilder withRetrieveVector(boolean with) {
            if (searchBuilder != null) {
                searchBuilder.withRetrieveVector(with);
            } else if (queryBuilder != null) {
                queryBuilder.withRetrieveVector(with);
            } else if (innerEmbeddingBuilder != null) {
                innerEmbeddingBuilder.withRetrieveVector(with);
            }
            return this;
        }

        public OperationBuilder withLimit(int limit) {
            if (searchBuilder != null) {
                searchBuilder.withLimit(limit);
            } else if (innerEmbeddingBuilder != null) {
                innerEmbeddingBuilder.withLimit(limit);
            } else if (queryBuilder != null) {
                queryBuilder.withLimit(limit);
            } else if (deleteBuilder != null) {
                throw new RuntimeException("删除操作不支持设置limit");
            } else if (updateBuilder != null) {
                throw new RuntimeException("更新操作不支持设置limit");
            }
            return this;
        }

        public OperationBuilder withOffset(int offset) {
            if (searchBuilder != null) {
                throw new RuntimeException("近似查询不支持分页");
            } else if (innerEmbeddingBuilder != null) {
                throw new RuntimeException("近似查询不支持分页");
            } else if (queryBuilder != null) {
                queryBuilder.withOffset(offset);
            } else if (deleteBuilder != null) {
                throw new RuntimeException("删除操作不支持分页");
            } else if (updateBuilder != null) {
                throw new RuntimeException("更新操作不支持分页");
            }
            return this;
        }

        public OperationBuilder withFilter(Filter filter) {
            if (searchBuilder != null) {
                searchBuilder.withFilter(filter);
            } else if (innerEmbeddingBuilder != null) {
                innerEmbeddingBuilder.withFilter(filter);
            } else if (queryBuilder != null) {
                queryBuilder.withFilter(filter);
            } else if (deleteBuilder != null) {
                deleteBuilder.withFilter(filter);
            } else if (updateBuilder != null) {
                updateBuilder.withFilter(filter);
            }
            return this;
        }


        public OperationBuilder withOutputFields(List<String> fields) {
            if (searchBuilder != null) {
                searchBuilder.withOutputFields(fields);
            } else if (innerEmbeddingBuilder != null) {
                innerEmbeddingBuilder.withOutputFields(fields);
            } else if (queryBuilder != null) {
                queryBuilder.withOutputFields(fields);
            }
            return this;
        }

        public OperationBuilder withDocumentIds(List<String> ids) {
            if (searchBuilder != null) {
                throw new RuntimeException("腾讯向量数据库相似性查询不支持设置id");
            } else if (innerEmbeddingBuilder != null) {
                throw new RuntimeException("腾讯向量数据库相似性查询不支持设置id");
            } else if (queryBuilder != null) {
                queryBuilder.withDocumentIds(ids);
            } else if (deleteBuilder != null) {
                deleteBuilder.withDocumentIds(ids);
            } else if (updateBuilder != null) {
                updateBuilder.withDocumentIds(ids);
            }
            return this;
        }

        public OperationBuilder withConsistencyLevel(ConsistencyLevel consistencyLevel) {

            //todo 腾讯sdk暂时未提供该参数的设置
            return this;
        }

        public List<Document> search(Collection collection) {
            if (queryBuilder != null) {
                return collection.query(queryBuilder.build());
            }
            if (searchBuilder != null) {
                return collection.search(searchBuilder.build())
                        .stream().flatMap(List::stream)
                        .collect(Collectors.toList());
            }
            if (innerEmbeddingBuilder != null) {
                return collection.searchByEmbeddingItems(innerEmbeddingBuilder.build()).getDocuments()
                        .stream().flatMap(List::stream)
                        .collect(Collectors.toList());
            }
            return null;
        }

        /**
         * 删除
         *
         * @param collection
         * @return
         */
        public int delete(Collection collection) {
            return (int) collection.delete(deleteBuilder.build()).getAffectedCount();
        }

        public int update(Collection collection, Document item) {
            return (int) collection.update(updateBuilder.build(), item).getAffectedCount();
        }


    }

}
